#include "BTreeNode.h"
#include <iostream>

using namespace std;

//testing
RC BTLeafNode::printLeaf()
{
	int i ;
	for(i= 3; i < (PageFile::PAGE_SIZE/sizeof(int)); i+=3)
	{
		cout<<"["<<i/3<<"] -> "<<buffer[i-3]<<" "<<buffer[i-2]<<" "<<buffer[i-1]<<", ";
	}
	cout<<endl<<"keyCount: "<<buffer[i-3]<<"	siblingPid: "<<buffer[i-2]<<endl<<endl;
}

//testing
RC BTNonLeafNode::printNonLeaf()
{
	int i ;
	cout<<"[0] -> "<<buffer[0]<<", ";
	for(i= 3; i < (PageFile::PAGE_SIZE/sizeof(int)); i+=2)
	{
		cout<<"["<<i/2<<"] -> "<<buffer[i-2]<<" "<<buffer[i-1]<<", ";
	}
	cout<<endl<<"keyCount: "<<buffer[i-1]<<endl<<endl;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{ 
	if(pf.read(pid, buffer) != 0){
		return RC_FILE_READ_FAILED;}
	keyCount = buffer[(PageFile::PAGE_SIZE/sizeof(int)) -2];	
	return 0; }
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{ 
	buffer[(PageFile::PAGE_SIZE/sizeof(int)) -2] = keyCount;	
	//testing
	//cout<<"pid: "<<pid<<endl;
	//printLeaf();

	if(pf.write(pid, buffer) != 0){
		return RC_FILE_WRITE_FAILED;}
	return 0; }

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{ return keyCount; }

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid) //need to check that the count has not reached it's limit before call this function.
{ 
	int eid;
	locate(key, eid);
	if(eid == -1)
	{
		buffer[keyCount*3 + 0] = rid.sid;
		buffer[keyCount*3 + 1] = rid.pid;
		buffer[keyCount*3 + 2] = key;
	}
	else{
		int i;
		for(i=keyCount*3; i >= eid*3+1; i--)
		{
			buffer[i+2]=buffer[i-1]; 
		}
		buffer[i+0]=rid.sid;
		buffer[i+1]=rid.pid;
		buffer[i+2]=key;
	}
	keyCount++;
return 0; }

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{ 
	int eid;
	locate(key, eid);
	RecordId r;
	int i;
	for(i=keyCount/2; i < keyCount; i++)
	{
		r.pid = buffer[i*3 + 1];
		r.sid = buffer[i*3];
		sibling.insert(buffer[i*3 + 2],r);  //might not need insert because they are already in order
		buffer[i*3] = -1;
		buffer[i*3 + 1] = -1;
		buffer[i*3 + 2] = -1;
	}
	keyCount = keyCount - (i-keyCount/2);
	if(eid == -1 || eid > keyCount)
		sibling.insert(key, rid);
	else
		insert(key,rid);

	siblingKey = sibling.buffer[2]; //get the sibling key to return

	sibling.buffer[(PageFile::PAGE_SIZE/sizeof(int)) -2] = (i-keyCount/2);	//setting keycount
	return 0; }

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{ 
	for(int i=2; i<keyCount*3; i+=3)
	{
		if(buffer[i]>=searchKey)
		{
			eid=(i-2)/3;
			return 0;
		}
	}
	eid = -1;
	return RC_NO_SUCH_RECORD;
 }

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{ 
	if (eid >keyCount)
	{
		return RC_NO_SUCH_RECORD;
	}
	
	rid.sid = buffer[0+3*eid];
	rid.pid = buffer[1+3*eid];
	key = buffer[2+3*eid];

	
	return 0; 
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{ 
	return buffer[((PageFile::PAGE_SIZE)/sizeof(int)) - 1]; 
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{	
	if(pid < 0)
	{
		return RC_INVALID_PID;
	}
	
	buffer[PageFile::PAGE_SIZE/sizeof(int) - 1 ]=pid;
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{ 
	if(pf.read(pid, buffer) != 0){
		return RC_FILE_READ_FAILED;}
	keyCount = buffer[(PageFile::PAGE_SIZE/sizeof(int)) -1];	
	return 0; }
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{ 
	buffer[(PageFile::PAGE_SIZE/sizeof(int)) -1] = keyCount;
	//testing
	//cout<<"pid: "<<pid<<endl;
	//printNonLeaf();

	if(pf.write(pid, buffer) != 0){
		return RC_FILE_WRITE_FAILED;}
	return 0; }

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{ return keyCount; }


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{ 
	int eid;
	if(locate(key, eid) == -1)
	{
		buffer[keyCount*2 + 1] = key;
		buffer[keyCount*2 + 2] = pid;
	}
	else{
		int i;
		for(i=keyCount*2; i >= eid*2+1; i--)
		{
			buffer[i+2]=buffer[i];
		}
		buffer[i+2]=pid;
		buffer[i+1]=key;
	}
	keyCount++;
	return 0; }

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{ 
	int eid;
	locate(key, eid);
	int i;
	for(i=(keyCount/2); i < keyCount; i++)
	{
		if(i == keyCount/2) //first iterations
		{
			midKey = buffer[i*2 + 1];
			sibling.buffer[0] = buffer[i*2 + 2]; //store pid to new buffer
		}
		else
		{
			sibling.insert(buffer[i*2 + 1],buffer[i*2 + 2]); //might not need insert because they are already in order
		}
		buffer[i*2 + 1] = -1;
		buffer[i*2 + 2] = -1;
	}
	keyCount = keyCount - (i-keyCount/2);

	if(eid == -1 || eid > keyCount) //check this and where do you insert if not this case
		sibling.insert(key, pid);
	else
		insert(key,pid);

	sibling.buffer[(PageFile::PAGE_SIZE/sizeof(int)) -1] = (i-keyCount/2)-1;	//setting keycount
	return 0; }

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
	int eid;
	
	if(locate(searchKey, eid) == -1)
	{
		pid = buffer[keyCount*2]; 
	}
	else
		pid = buffer[eid*2];
	return 0; 
	}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{ 
	buffer[0] = pid1;
	buffer[1] = key;
	buffer[2] = pid2;
	keyCount++;			//addition!!!
	return 0; }

int BTNonLeafNode::locate(int searchKey, int& eid)
{ 
	for(int i=1; i<keyCount*2; i+=2)
	{
		if(buffer[i]>searchKey)
		{
			eid=(i-1)/2;
			return 0;
		}
	}
	eid = -1;
	return -1;
 }
